package com.lujieni.springboothutool.utils;

import java.lang.invoke.SerializedLambda;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;

/**
 * 比较
 *
 * @author 陈正
 * @date 2025/08/14 14:42
 **/
public class CompareUtil<T> {

    private List<Function<T, ?>> fieldList = new ArrayList<>(32);

    public static int compare(Integer a, Integer b) {
        return Integer.compare(a, b);
    }

    public static int compare(Long a, Long b) {
        return Long.compare(a, b);
    }

    public static int compare(Double a, Double b) {
        return Double.compare(a, b);
    }

    public static int compare(String a, String b) {
        return a.compareTo(b);
    }

    public static int compare(BigDecimal a, BigDecimal b) {
        return a.compareTo(b);
    }

    public CompareUtil<T> thenComparing(Function<T, ?> field) {
        return comparingField(field);
    }

    public CompareUtil<T> comparingField(Function<T, ?> field) {
        fieldList.add(field);
        return this;
    }

    public static <T> String compareByFieldsString(T a, T b, CompareUtil<T> comparators) {
        StringBuilder errorMsg = new StringBuilder();
        for (Function<T, ?> field : comparators.fieldList) {
            int check = compareNullSafe(field.apply(a), field.apply(b));
            if (check != 0) {
                errorMsg.append("[").append(getFieldName(field)).append(":").append(String.valueOf(field.apply(a))).append("->").append(String.valueOf(field.apply(b))).append("]");
            }
        }
        return errorMsg.toString();
    }

    /**
     * 获取实体类的字段名称
     */
    public static <T> String getFieldName(Function<T, ?> fn) {
        SerializedLambda serializedLambda = getSerializedLambda(fn);
        // 从lambda信息取出method、field、class等
        String fieldName = serializedLambda.getImplMethodName().substring("get".length());
        fieldName = fieldName.replaceFirst(fieldName.charAt(0) + "", (fieldName.charAt(0) + "").toLowerCase());
        Field field;
        try {
            field = Class.forName(serializedLambda.getImplClass().replace("/", ".")).getDeclaredField(fieldName);
        } catch (ClassNotFoundException | NoSuchFieldException e) {
            throw new RuntimeException(e);
        }

        return field.getName();
    }

    private static <T> SerializedLambda getSerializedLambda(Function<T, ?> fn) {
        // 从function取出序列化方法
        Method writeReplaceMethod;
        try {
            writeReplaceMethod = fn.getClass().getDeclaredMethod("writeReplace");
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        }

        // 从序列化方法取出序列化的lambda信息
        boolean isAccessible = writeReplaceMethod.isAccessible();
        writeReplaceMethod.setAccessible(true);
        SerializedLambda serializedLambda;
        try {
            serializedLambda = (SerializedLambda) writeReplaceMethod.invoke(fn);
        } catch (IllegalAccessException | InvocationTargetException e) {
            throw new RuntimeException(e);
        }
        writeReplaceMethod.setAccessible(isAccessible);
        return serializedLambda;
    }

    public static <T> boolean compareByFields(T a, T b, CompareUtil<T> comparators) {
        for (Function<T, ?> field : comparators.fieldList) {
            Object param1 = field.apply(a);
            Object param2 = field.apply(b);
            int check = compareNullSafe(param1, param2);
            if (check != 0) {
                return false;
            }
        }
        return true;
    }

    public static int compareNullSafe(Object a, Object b) {
        if (a == null && b == null) return 0;
        if (a == null) return 1;
        if (b == null) return -1;
        if (a instanceof Integer) {
            return compare((Integer) a, (Integer) b);
        }
        if (a instanceof Long) {
            return compare((Long) a, (Long) b);
        }
        if (a instanceof Double) {
            return compare((Double) a, (Double) b);
        }
        if (a instanceof String) {
            return compare((String) a, (String) b);
        }
        if (a instanceof BigDecimal) {
            return compare((BigDecimal) a, (BigDecimal) b);
        }
        return compare(String.valueOf(a), String.valueOf(b));
    }

}
